import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Enumeration;

public class SqlTextPane extends JTextPane {

    private DefaultStyledDocument styledDocument;

    private MutableAttributeSet keyAttributeSet, normalAttributeSet;
    private MutableAttributeSet commentsAttributeSet;
    private String[] keywords = {
            "select", "update", "delete",
            "from", "where", "by", "order",
            "group", "limit", "as",
            "left", "join", "inner",
            "on", "distinct", "case", "then",
            "unique", "union", "exists", "not", "in"
    };

    public SqlTextPane() {
        super();
        styledDocument = new DefaultStyledDocument(new StyleContext());
        this.setDocument(styledDocument);
        this.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent ke) {
                if (KeyEvent.getKeyText(ke.getKeyCode()).length() > 0) {
                    syntaxDocument();
                }
            }
        });

        final SqlTextPane ref = this;
        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == 3) {
                    JPopupMenu popupMenu = new JPopupMenu();
                    final String selectedText = ref.getSelectedText();

                    //beatify
                    JMenuItem beatifySQL = new JMenuItem("Beatify SQL") {
                        @Override
                        protected void fireActionPerformed(ActionEvent event) {
                            formatSelected(selectedText);
                        }
                    };
                    beatifySQL.setEnabled(selectedText != null && !"".equals(selectedText));
                    popupMenu.add(beatifySQL);

                    //beatify
                    JMenuItem join = new JMenuItem("Join") {
                        @Override
                        protected void fireActionPerformed(ActionEvent event) {
                            join(selectedText);
                        }
                    };
                    beatifySQL.setEnabled(selectedText != null && !"".equals(selectedText));
                    popupMenu.add(join);

                    // insert
                    popupMenu.add(new JMenuItem("Insert SELECT") {
                        @Override
                        protected void fireActionPerformed(ActionEvent event) {
                            insertText("SELECT * FROM table");
                        }
                    });

                    popupMenu.show(ref, e.getX(), e.getY());
                }
            }
        });

        //定义关键字显示属性
        keyAttributeSet = new SimpleAttributeSet();
        StyleConstants.setForeground(keyAttributeSet, Color.blue);
        StyleConstants.setBold(keyAttributeSet, true);

        //定义一般文本显示属性
        normalAttributeSet = new SimpleAttributeSet();
        StyleConstants.setForeground(normalAttributeSet, Color.black);


        //定义一般文本显示属性
        commentsAttributeSet = new SimpleAttributeSet();
        StyleConstants.setForeground(commentsAttributeSet, Color.gray);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("test text pane");
        frame.getContentPane().add(new SqlTextPane());
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        final int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setBounds(inset, inset, screenSize.width - inset * 2, screenSize.height - inset * 2);
        frame.setVisible(true);
    }


    /**
     * ssss
     *
     * @param element
     */
    private void syntaxElement(Element element) {
        int startOffset = element.getStartOffset();
        int endOffset = element.getEndOffset();

        String text;
        try {
            text = styledDocument.getText(startOffset, endOffset - startOffset);
        } catch (BadLocationException e) {
            throw new RuntimeException(e);
        }
        styledDocument.setCharacterAttributes(startOffset, text.length(), normalAttributeSet, false);

        SqlTokenizer st = new SqlTokenizer(text);
        while (st.hasMoreElements()) {
            int begin = st.getCurPosition();
            String token = st.nextElement();
            if (isKeyword(token)) {
                styledDocument.setCharacterAttributes(startOffset + begin, token.length(), keyAttributeSet, false);
            } else if (token.startsWith("--")) {
                styledDocument.setCharacterAttributes(startOffset + begin, token.length(), commentsAttributeSet, false);
            }
        }
    }

    private void syntaxDocument() {
        Element root = styledDocument.getDefaultRootElement();
        int line = root.getElementIndex(getCaretPosition());
        Element lineElement = root.getElement(line);
        syntaxElement(lineElement);
    }

    private void syntaxAll() {
        Element[] elements = styledDocument.getRootElements();
        for (Element element : elements) {
            syntaxElement(element);
        }
    }

    private boolean isKeyword(String input) {
        for (String keyword : keywords) {
            if (keyword.equalsIgnoreCase(input)) {
                return true;
            }
        }
        return false;
    }


    private void insertText(String content) {
        try {
            styledDocument.insertString(styledDocument.getLength(), "\n" + content, null);
            syntaxDocument();
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void formatSelected(String selectedText) {
        if (!"".equals(selectedText)) {
            String format = new BasicFormatter().format(selectedText).trim();
            this.replaceSelection(format);

            syntaxAll();
        }
    }

    private void join(String selectedText) {
        if (!"".equals(selectedText)) {
            String format = selectedText.replaceAll("\n", "");
            this.replaceSelection(format);

            syntaxDocument();
        }
    }
}

class SqlTokenizer implements Enumeration<String> {

    private String sql;

    private int curPosition;
    private int maxPosition;
    private int newPosition;
    private char[] delimiters = new char[]{
            ' ', ';', '\n'
    };

    SqlTokenizer(String sql) {
        this.sql = sql;
        curPosition = 0;
        newPosition = 0;
        maxPosition = sql.length();
    }

    public static void main(String[] args) {
        String sql = "hahaha -- hello\n hahaha --test ";
        SqlTokenizer tokenizer = new SqlTokenizer(sql);
        int i = 0;
        while (tokenizer.hasMoreElements()) {
            System.out.println((i++) + ">>" + tokenizer.nextElement());
        }
    }

    @Override
    public boolean hasMoreElements() {
        movePosition();
        return curPosition < maxPosition;
    }

    private void movePosition() {
        while (curPosition < maxPosition) {
            if (!isDelimiter(sql.charAt(curPosition))) {
                newPosition = curPosition;
                break;
            } else {
                curPosition++;
            }
        }
    }

    @Override
    public String nextElement() {

        while (curPosition < maxPosition && !isDelimiter(sql.charAt(curPosition))) {
            curPosition++;
        }

        String str = sql.substring(newPosition, curPosition);
        if ("--".startsWith(str)) {

            while (curPosition <= maxPosition) {

                if ('\n' == sql.charAt(curPosition)) {
                    str = sql.substring(newPosition, curPosition);
                    newPosition = curPosition;
                    curPosition++;
                    break;
                } else {
                    curPosition++;
                }
            }

        } else {
            newPosition = curPosition;
            curPosition++;
        }

        return str;
    }

    private boolean isDelimiter(char aChar) {
        for (char delimiter : delimiters) {
            if (aChar == delimiter) {
                return true;
            }
        }
        return false;
    }

    int getCurPosition() {
        return curPosition;
    }
}